package com.hsteach.service.cookmenu;

import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

import com.google.common.collect.Lists;
import com.hsteach.common.core.domain.model.AuthInfo;
import com.hsteach.common.exception.ServiceException;
import com.hsteach.common.utils.DateUtils;
import com.hsteach.common.utils.DistinctKeyUtil;
import com.hsteach.common.utils.SecurityUtils;
import com.hsteach.common.utils.StringUtils;
import com.hsteach.common.utils.poi.ExcelUtil;
import com.hsteach.dao.domain.cookmenu.Cookmenu;
import com.hsteach.dao.domain.cookmenu.CookmenuLikes;
import com.hsteach.dao.mapper.cookmenu.CookmenuLikesMapper;
import com.hsteach.dao.mapper.cookmenu.CookmenuMapper;
import com.hsteach.enums.BooleanEnum;
import com.hsteach.service.third.XgkService;
import com.hsteach.util.ChineseNumToArabicNumUtil;
import com.hsteach.web.controller.common.vo.SemMonth;
import com.hsteach.web.controller.cookmenu.dto.*;
import com.hsteach.web.controller.cookmenu.vo.DishExcel;
import com.hsteach.web.controller.cookmenu.vo.DishVo;
import com.hsteach.web.controller.cookmenu.vo.ListMenuVo;
import com.hsteach.web.controller.cookmenu.vo.SemWeek;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.hsteach.dao.mapper.cookmenu.CookmenuWeekMapper;
import com.hsteach.dao.domain.cookmenu.CookmenuWeek;
import org.springframework.transaction.annotation.Transactional;

/**
 * 食堂每周菜谱明细Service业务层处理
 * 
 * @author fanxquer
 * @date 2023-11-05
 */
@Service
public class CookmenuWeekService
{
    @Autowired
    private CookmenuWeekMapper cookmenuWeekMapper;

    @Autowired
    private CookmenuMapper cookmenuMapper;

    @Autowired
    private CookmenuLikesMapper cookmenuLikesMapper;

    @Autowired
    private XgkService xgkService;

    /**
     * 查询食堂每周菜谱明细
     * 
     * @param id 食堂每周菜谱明细主键
     * @return 食堂每周菜谱明细
     */
    public CookmenuWeek selectCookmenuWeekById(Long id)
    {
        return cookmenuWeekMapper.selectCookmenuWeekById(id);
    }

    /**
     * 查询食堂每周菜谱明细列表
     * 
     * @param cookmenuWeek 食堂每周菜谱明细
     * @return 食堂每周菜谱明细
     */
    public List<CookmenuWeek> selectCookmenuWeekList(CookmenuWeek cookmenuWeek)
    {
        return cookmenuWeekMapper.selectCookmenuWeekList(cookmenuWeek);
    }

    public List<ListMenuVo> selectCookmenuWeekList(ListDishDto dto)
    {
        AuthInfo authInfo = SecurityUtils.getAuthInfo();
        CookmenuWeek query = new CookmenuWeek();
        query.setDelStatus(BooleanEnum.TRUE.getCode());
        query.setOrgCode(authInfo.getOrgCode());
        query.setWeek(dto.getWeekIndex());
        query.setSemId(dto.getSemId());
        List<CookmenuWeek> list = cookmenuWeekMapper.selectCookmenuWeekList(query);

        // 查询点赞数
        Cookmenu queryDish = new Cookmenu();
        queryDish.setDelStatus(BooleanEnum.TRUE.getCode());
        queryDish.setOrgCode(authInfo.getOrgCode());
        List<Cookmenu> cookmenus = cookmenuMapper.selectCookmenuList(queryDish);
        Map<String, Long> likesMap = cookmenus.stream().collect(Collectors.toMap(Cookmenu::getDishName, Cookmenu::getLikes, (v1, v2) -> v2));
        Map<String, String> imgMap = cookmenus.stream().collect(Collectors.toMap(Cookmenu::getDishName, cookmenu -> cookmenu.getDishImg() == null?"":cookmenu.getDishImg(), (v1, v2) -> v2));
        // 查询点赞过的菜品
        Set<String> dishNames = list.stream().map(CookmenuWeek::getDishName).collect(Collectors.toSet());
        CookmenuLikes likeQuery = new CookmenuLikes();
        likeQuery.setOrgCode(authInfo.getOrgCode());
        likeQuery.setDishNames(Lists.newArrayList(dishNames));
        likeQuery.setPersonId(authInfo.getPersonId());
        List<CookmenuLikes> cookmenuLikes = cookmenuLikesMapper.selectCookmenuLikesList(likeQuery);
        List<String> likedDishs = cookmenuLikes.stream().map(CookmenuLikes::getDishName).collect(Collectors.toList());
        // 组装返回数据
        List<ListMenuVo> ret = list.stream().collect(Collectors.groupingBy(CookmenuWeek::getDayOfWeek))
                .entrySet().stream().map(e -> {
                    ListMenuVo vo = new ListMenuVo();
                    vo.setDayOfWeek(e.getKey());
                    List<DishVo> dish = e.getValue().stream().map(k -> {
                        DishVo dishVo = new DishVo();
                        BeanUtils.copyProperties(k, dishVo);
                        dishVo.setLikes(likesMap.getOrDefault(k.getDishName(), 0L));
                        dishVo.setLiked(likedDishs.contains(k.getDishName()));
                        if (imgMap.containsKey(k.getDishName())) {
                            dishVo.setDishImg(imgMap.get(k.getDishName()));
                        }
                        return dishVo;
                    }).collect(Collectors.toList());
                    vo.setDishes(dish);
                    return vo;
                }).collect(Collectors.toList());

        return ret;
    }

    /**
     * 新增食堂每周菜谱明细
     * 
     * @param cookmenuWeek 食堂每周菜谱明细
     * @return 结果
     */
    public int insertCookmenuWeek(CookmenuWeek cookmenuWeek)
    {
        cookmenuWeek.setCreateTime(DateUtils.getNowDate());
        return cookmenuWeekMapper.insertCookmenuWeek(cookmenuWeek);
    }

    public Boolean importFile(ImportDishDto dto, InputStream is) throws Exception {
        AuthInfo authInfo = SecurityUtils.getAuthInfo();
        ExcelUtil<DishExcel> util = new ExcelUtil<>(DishExcel.class);
        Map<String, List<DishExcel>> allData = util.importAll(is, 0);
        List<SemWeek> weekList = getWeekList(dto.getBeginDay(), dto.getEndDay());
        Map<Integer, SemWeek> weekMap = weekList.stream().collect(Collectors.toMap(SemWeek::getWeekIndex, e -> e, (v1, v2) -> v2));

        List<String> msgs = Lists.newArrayList();
        List<CookmenuWeek> cookmenuWeeks = allData.entrySet().stream().flatMap(e -> {
            List<CookmenuWeek> dishs = Lists.newArrayList();
            String key = e.getKey();
            String chNum = key.replaceAll("第", "").replaceAll("周", "").trim();
            int week;
            if (chNum.matches("^\\d+$")) {
                week = Integer.valueOf(chNum);
            } else {
                week = ChineseNumToArabicNumUtil.exChToNum(chNum);
            }
            if (week == -1) {
                msgs.add("sheet " + key + "名称有误，无法导入");
                return dishs.stream();
            }
            if (weekMap.containsKey(week)) {
                for (int j = 0; j < e.getValue().size(); j++) {
                    dishs.addAll(genData(e.getValue().get(j).getBreakfast(), week, j + 1, 1, dto.getSemId(), dto.getSemName(), authInfo, msgs));
                    dishs.addAll(genData(e.getValue().get(j).getLunch(), week, j + 1, 2, dto.getSemId(), dto.getSemName(), authInfo, msgs));
                    dishs.addAll(genData(e.getValue().get(j).getDinner(), week, j + 1, 3, dto.getSemId(), dto.getSemName(), authInfo, msgs));
                    dishs.addAll(genData(e.getValue().get(j).getSupper(), week, j + 1, 4, dto.getSemId(), dto.getSemName(), authInfo, msgs));
                }
            } else {
                msgs.add("基础数据不存在" + key + "，sheet " + key + " 无法导入");
            }
            return dishs.stream();
        }).collect(Collectors.toList());
        if (cookmenuWeeks.isEmpty()) {
            if (!msgs.isEmpty()) {
                String msg = String.join("\r\n", msgs);
                throw new Exception(msg);
            }

            return true;
        }
        Set<String> dishNames = cookmenuWeeks.stream().map(CookmenuWeek::getDishName).collect(Collectors.toSet());
        Cookmenu query = new Cookmenu();
        query.setDelStatus(BooleanEnum.TRUE.getCode());
        query.setOrgCode(authInfo.getOrgCode());
        query.setDishNames(Lists.newArrayList(dishNames));
        List<Cookmenu> cookmenus = cookmenuMapper.selectCookmenuList(query);
        if (cookmenus.size() != dishNames.size()) {
            List<String> hasNames = cookmenus.stream().map(k -> k.getDishName()).collect(Collectors.toList());
            List<Cookmenu> insertData = dishNames.stream().filter(k -> !hasNames.contains(k)).map(k -> {
                Cookmenu cookmenu = new Cookmenu();
                cookmenu.setDelStatus(BooleanEnum.TRUE.getCode());
                cookmenu.setOrgCode(authInfo.getOrgCode());
                cookmenu.setDishName(k);
                cookmenu.setLikes(0L);
                cookmenu.setCreateByName(authInfo.getName());
                cookmenu.setCreateBy(authInfo.getPersonId());
                return cookmenu;
            }).collect(Collectors.toList());
            cookmenuMapper.batchInsert(insertData);
        }

        Set<Integer> weeks = cookmenuWeeks.stream().map(CookmenuWeek::getWeek).collect(Collectors.toSet());
        cookmenuWeekMapper.deleteByWeekAndSemId(Lists.newArrayList(weeks), dto.getSemId(), authInfo.getOrgCode());
        cookmenuWeekMapper.batchInsert(cookmenuWeeks);

        if (!msgs.isEmpty()) {
            String msg = String.join("\r\n", msgs);
            throw new Exception(msg);
        }

        return true;
    }

    private List<CookmenuWeek> genData(String dishStr, Integer week, Integer dayOfWeek, Integer dishType, String semId, String semName, AuthInfo authInfo, List<String> msgs) {
        if (StringUtils.isEmpty(dishStr)) {
            return Lists.newArrayList();
        }
        String[] split = dishStr.split("、");
        if (split.length > 100) {
            msgs.add("第" + week + "周" + DateUtils.getweekDayName(dayOfWeek)  + "菜品超过100个，无法导入");
            return Lists.newArrayList();
        }
        List<CookmenuWeek> ret = Lists.newArrayList();
        for (int i = 0; i < split.length; i++) {
            if (split[i].length() > 32) {
                msgs.add("第" + week + "周" + DateUtils.getweekDayName(dayOfWeek) + split[i] + "菜品名过长，请勿超过32个字");
                continue;
            }
            CookmenuWeek cookmenuWeek = new CookmenuWeek();
            cookmenuWeek.setOrgCode(authInfo.getOrgCode());
            cookmenuWeek.setDelStatus(BooleanEnum.TRUE.getCode());
            cookmenuWeek.setDayOfWeek(dayOfWeek);
            cookmenuWeek.setWeek(week);
            cookmenuWeek.setSemId(semId);
            cookmenuWeek.setSemName(semName);
            cookmenuWeek.setDishType(dishType);
            cookmenuWeek.setDishName(split[i]);
            cookmenuWeek.setCreateByName(authInfo.getName());
            cookmenuWeek.setCreateBy(authInfo.getPersonId());
            ret.add(cookmenuWeek);
        }

        // 去重
        ret = ret.stream().filter(DistinctKeyUtil.distinctByKey(CookmenuWeek::getDishName)).collect(Collectors.toList());

        return ret;
    }

    /**
     * 修改食堂每周菜谱明细
     * 
     * @param cookmenuWeek 食堂每周菜谱明细
     * @return 结果
     */
    public int updateCookmenuWeek(CookmenuWeek cookmenuWeek)
    {
        cookmenuWeek.setUpdateTime(DateUtils.getNowDate());
        return cookmenuWeekMapper.updateCookmenuWeek(cookmenuWeek);
    }

    @Transactional(rollbackFor = Exception.class)
    public int updateDish(UpdateDishDto dto)
    {
        CookmenuWeek cookmenuWeek = cookmenuWeekMapper.selectCookmenuWeekById(dto.getId());
        if (cookmenuWeek == null) {
            throw new ServiceException("未查找到食谱配置");
        }
        // 修改菜品图片
        Cookmenu query = new Cookmenu();
        query.setDishName(cookmenuWeek.getDishName());
        query.setDelStatus(BooleanEnum.TRUE.getCode());
        query.setOrgCode(SecurityUtils.getAuthInfo().getOrgCode());
        List<Cookmenu> cookmenus = cookmenuMapper.selectCookmenuList(query);
        if (!cookmenus.isEmpty()) {
            Cookmenu cookmenu = cookmenus.get(0);
            cookmenu.setDishImg(dto.getDishImg());
            cookmenuMapper.updateCookmenu(cookmenu);
        }

        // 改名需要继承点赞数，已有菜品就不继承
        if (!cookmenuWeek.getDishName().equals(dto.getDishName())) {
            if (cookmenus.isEmpty()) {
                query.setLikes(0L);
                query.setCreateBy(SecurityUtils.getAuthInfo().getPersonId());
                cookmenuMapper.insertCookmenu(query);
            }
        }
        cookmenuWeek.setDishName(dto.getDishName());
        cookmenuWeek.setDishImg(dto.getDishImg());
        cookmenuWeek.setUpdateTime(DateUtils.getNowDate());
        return cookmenuWeekMapper.updateCookmenuWeek(cookmenuWeek);
    }

    @Transactional(rollbackFor = Exception.class)
    public Boolean copyDish(CopyDishDto dto) {
        AuthInfo authInfo = SecurityUtils.getAuthInfo();
        CookmenuWeek query = new CookmenuWeek();
        query.setDelStatus(BooleanEnum.TRUE.getCode());
        query.setOrgCode(authInfo.getOrgCode());
        query.setSemId(dto.getSemId());
        query.setWeek(dto.getFromWeekIndex());
        List<CookmenuWeek> cookmenuWeeks = cookmenuWeekMapper.selectCookmenuWeekList(query);
        if (cookmenuWeeks.isEmpty()) {
            throw new ServiceException("复制周数据为空");
        }

        // 删除周数据
        cookmenuWeekMapper.deleteByWeekAndSemId(dto.getToWeekIndexs(), dto.getSemId(), authInfo.getOrgCode());
        List<CookmenuWeek> datas = dto.getToWeekIndexs().stream().flatMap(e -> {
            return cookmenuWeeks.stream().map(k -> {
                CookmenuWeek week = new CookmenuWeek();
                BeanUtils.copyProperties(k, week);
                week.setCreateBy(authInfo.getPersonId());
                week.setCreateTime(new Date());
                week.setUpdateBy(null);
                week.setUpdateTime(null);
                week.setWeek(e);
                return week;
            });
        }).collect(Collectors.toList());

        cookmenuWeekMapper.batchInsert(datas);

        return true;
    }

    /**
     * 批量删除食堂每周菜谱明细
     * 
     * @param id 需要删除的食堂每周菜谱明细主键
     * @return 结果
     */
    public int deleteCookmenuWeekById(Long id)
    {
        CookmenuWeek update = new CookmenuWeek();
        update.setId(id);
        update.setDelStatus(BooleanEnum.FALSE.getCode());
        return cookmenuWeekMapper.updateCookmenuWeek(update);
    }

    @Transactional(rollbackFor = Exception.class)
    public int notlike(LikeDto dto)
    {
        AuthInfo authInfo = SecurityUtils.getAuthInfo();
        Cookmenu query = new Cookmenu();
        query.setDishName(dto.getDishName());
        query.setDelStatus(BooleanEnum.TRUE.getCode());
        query.setOrgCode(authInfo.getOrgCode());
        List<Cookmenu> cookmenus = cookmenuMapper.selectCookmenuList(query);
        if (cookmenus.size() == 0) {
            throw new ServiceException("未找到菜品");
        }
        Cookmenu cookmenu = cookmenus.get(0);
        CookmenuLikes likeQuery = new CookmenuLikes();
        likeQuery.setDishNames(Lists.newArrayList(dto.getDishName()));
        likeQuery.setPersonId(authInfo.getPersonId());
        likeQuery.setOrgCode(authInfo.getOrgCode());
        List<CookmenuLikes> cookmenuLikes = cookmenuLikesMapper.selectCookmenuLikesList(likeQuery);
        if (cookmenuLikes.size() == 0) {
            throw new ServiceException("没有点赞记录");
        }

        List<Long> ids = cookmenuLikes.stream().map(CookmenuLikes::getId).collect(Collectors.toList());
        cookmenuLikesMapper.deleteCookmenuLikesByIds(ids);
        cookmenu.setLikes(Optional.ofNullable(cookmenu.getLikes()).orElse(0L) - 1);

        return cookmenuMapper.updateCookmenu(cookmenu);
    }

    @Transactional(rollbackFor = Exception.class)
    public int like(LikeDto dto)
    {
        AuthInfo authInfo = SecurityUtils.getAuthInfo();
        Cookmenu query = new Cookmenu();
        query.setDishName(dto.getDishName());
        query.setDelStatus(BooleanEnum.TRUE.getCode());
        query.setOrgCode(authInfo.getOrgCode());
        List<Cookmenu> cookmenus = cookmenuMapper.selectCookmenuList(query);
        if (cookmenus.size() == 0) {
            throw new ServiceException("未找到菜品");
        }
        Cookmenu cookmenu = cookmenus.get(0);
        CookmenuLikes likeQuery = new CookmenuLikes();
        likeQuery.setDishNames(Lists.newArrayList(dto.getDishName()));
        likeQuery.setPersonId(authInfo.getPersonId());
        likeQuery.setOrgCode(authInfo.getOrgCode());
        List<CookmenuLikes> cookmenuLikes = cookmenuLikesMapper.selectCookmenuLikesList(likeQuery);
        if (cookmenuLikes.size() > 0) {
            throw new ServiceException("不能重复点赞");
        }

        likeQuery.setDishName(dto.getDishName());
        cookmenuLikesMapper.insertCookmenuLikes(likeQuery);

        cookmenu.setLikes(Optional.ofNullable(cookmenu.getLikes()).orElse(0L) + 1);

        return cookmenuMapper.updateCookmenu(cookmenu);
    }

    public List<SemWeek> getWeekList(Date beginDay, Date endDay) {
        Calendar begin = Calendar.getInstance();
        begin.setTime(beginDay);
        Calendar end = Calendar.getInstance();
        end.setTime(endDay);

        List<SemWeek> semWeeks = Lists.newArrayList();
        int weekIndex = 0;
        while (!begin.after(end)) {
            weekIndex++;
            SemWeek week = new SemWeek();
            week.setBeginDay(begin.getTime());
            week.setWeekIndex(weekIndex);

            int offset = 7;
            int dayOfWeek = DateUtils.getDayOfWeek(begin);
            week.setBeginDayOfWeek(dayOfWeek);
            if (dayOfWeek != 1) {
                offset = 8 - DateUtils.getDayOfWeek(begin);
            }

            begin.add(Calendar.DATE, offset);

            if (begin.after(end)) {
                week.setEndDay(endDay);
                week.setEndDayOfWeek(DateUtils.getDayOfWeek(end));
                semWeeks.add(week);
                break;
            } else {
                begin.add(Calendar.DATE, -1);
                week.setEndDay(begin.getTime());
                begin.add(Calendar.DATE, 1);
                week.setEndDayOfWeek(7);
                semWeeks.add(week);
            }
        }

        // 选中当前周
        if (!semWeeks.isEmpty()) {
            long now = System.currentTimeMillis();
            if (now < beginDay.getTime() || now > endDay.getTime()) {
                semWeeks.get(0).setSelected(true);
            } else {
                for (SemWeek week: semWeeks) {
                    if (now >= week.getBeginDay().getTime() && now < week.getEndDay().getTime()) {
                        week.setSelected(true);
                    }
                }
            }
        }

        return semWeeks;
    }

    public List<SemMonth> getMonthList(Date beginDay, Date endDay) {
        Calendar begin = Calendar.getInstance();
        begin.setTime(beginDay);
        Calendar end = Calendar.getInstance();
        end.setTime(endDay);

        List<SemMonth> semMonths = Lists.newArrayList();
        while (!begin.after(end)) {
            SemMonth month = new SemMonth();
            month.setBeginDay(begin.getTime());
            month.setMonthIndex(begin.get(Calendar.MONTH) + 1);

            month.setBeginDayOfMonth(begin.get(Calendar.DAY_OF_MONTH));

            begin.add(Calendar.MONTH, 1);
            begin.set(Calendar.DAY_OF_MONTH, 1);

            if (begin.after(end)) {
                month.setEndDay(endDay);
                month.setEndDayOfMonth(end.get(Calendar.DAY_OF_MONTH));
                semMonths.add(month);
                break;
            } else {
                begin.add(Calendar.DATE, -1);
                month.setEndDay(begin.getTime());
                month.setEndDayOfMonth(begin.get(Calendar.DAY_OF_MONTH));
                begin.add(Calendar.DATE, 1);

                semMonths.add(month);
            }
        }

        // 选中当前月
        if (!semMonths.isEmpty()) {
            long now = System.currentTimeMillis();
            if (now < beginDay.getTime() || now > endDay.getTime()) {
                semMonths.get(0).setSelected(true);
            } else {
                for (SemMonth month: semMonths) {
                    if (now >= month.getBeginDay().getTime() && now < month.getEndDay().getTime()) {
                        month.setSelected(true);
                    }
                }
            }
        }

        return semMonths;
    }

    public int addDish(AddDishDto dto) {
        AuthInfo authInfo = SecurityUtils.getAuthInfo();
        CookmenuWeek cookmenuWeek = new CookmenuWeek();
        cookmenuWeek.setOrgCode(authInfo.getOrgCode());
        cookmenuWeek.setDelStatus(BooleanEnum.TRUE.getCode());
        cookmenuWeek.setDayOfWeek(dto.getDayOfWeek());
        cookmenuWeek.setWeek(dto.getWeekIndex());
        cookmenuWeek.setSemId(dto.getSemId());
        cookmenuWeek.setDishType(dto.getDishType());
        List<CookmenuWeek> count = cookmenuWeekMapper.selectCookmenuWeekList(cookmenuWeek);
        if (count.size() >= 100) {
            throw  new ServiceException("请勿超过100个菜品");
        }
        cookmenuWeek.setDishName(dto.getDishName());
        List<CookmenuWeek> cookmenuWeeks = cookmenuWeekMapper.selectCookmenuWeekList(cookmenuWeek);
        if (cookmenuWeeks.size() > 0) {
            throw  new ServiceException("已有同名菜品");
        }

        cookmenuWeek.setWeek(dto.getWeekIndex());
        cookmenuWeek.setCreateByName(authInfo.getName());
        cookmenuWeek.setCreateBy(authInfo.getPersonId());
        BeanUtils.copyProperties(dto, cookmenuWeek);

        Cookmenu query = new Cookmenu();
        query.setDelStatus(BooleanEnum.TRUE.getCode());
        query.setOrgCode(authInfo.getOrgCode());
        query.setDishName(dto.getDishName());
        int i = cookmenuWeekMapper.insertCookmenuWeek(cookmenuWeek);
        List<Cookmenu> cookmenus = cookmenuMapper.selectCookmenuList(query);
        if (cookmenus.size() == 0) {
            query.setLikes(0L);
            query.setDishImg(dto.getDishImg());
            query.setCreateByName(authInfo.getName());
            query.setCreateBy(authInfo.getPersonId());
            cookmenuMapper.insertCookmenu(query);
        } else {
            // 更新菜品图片
            cookmenus.forEach(e -> {
                e.setDishImg(dto.getDishImg());
                cookmenuMapper.updateCookmenu(e);
            });
        }

        return i;

    }
}
